package com.meirengu.webview.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.bean.ProducerBean;
import com.kwd.rocketmq.RocketmqEvent;
import com.meirengu.common.RedisClient;
import com.meirengu.service.impl.BaseServiceImpl;
import com.meirengu.webview.dao.LmProductCashDao;
import com.meirengu.webview.dao.LmTrackEventDao;
import com.meirengu.webview.dao.LmTrackEventInfoDao;
import com.meirengu.webview.model.KwChannel;
import com.meirengu.webview.model.LmProductCash;
import com.meirengu.webview.model.LmTrackEvent;
import com.meirengu.webview.model.LmTrackEventInfo;
import com.meirengu.webview.service.KwChannelDataService;
import com.meirengu.webview.service.KwChannelService;
import com.meirengu.webview.service.LmProductCashService;
import com.meirengu.webview.service.LmTrackEventService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;

import static com.meirengu.utils.DateUtils.getDayBeginTime;
import static com.meirengu.utils.DateUtils.getDayEndTime;
import static java.util.Calendar.*;
import static org.apache.commons.lang3.StringUtils.*;

/**
 * LmTrackEvent服务实现层
 *
 * @author Mao
 * @create Sat Jun 09 15:33:36 CST 2018
 */
@Service
public class LmTrackEventServiceImpl extends BaseServiceImpl<LmTrackEvent> implements LmTrackEventService {

    private static final Logger logger = LoggerFactory.getLogger(LmTrackEventServiceImpl.class);

    @Autowired
    LmTrackEventDao lmTrackEventDao;

    @Autowired
    LmTrackEventInfoDao lmTrackEventInfoDao;

    @Autowired
    private KwChannelService kwChannelService;

    @Autowired
    private RedisClient redisClient;

    private final static String PREFIX_COUNT_CLICK = "web_count_click_";
    private final static String PREFIX_COUNT_IP = "web_count_ip_";
    private final static String PREFIX_COUNT_SESSION = "web_count_session_";
    private final static String PREFIX_COUNT_JSESSION = "web_count_jsession_id_";

    @Autowired
    private KwChannelDataService kwChannelDataService;

    @Autowired
    private LmProductCashDao lmProductCashDao;

    @Autowired
    private LmProductCashService lmProductCashService;

    @Value("${producter.topic.kwd.product}")
    private String topic;

    @Autowired
    ProducerBean producer;

    /**
     * 用户在贷款超市的前端界面上点击了一个第三方产品的处理过程
     * 1.先记录原始记录
     * 2.更新渠道的产品点击量
     * 3.查询当日记录，更新
     * 4.判断渠道是否下架
     * 每个逻辑独立，互不影响
     *
     * @param cid 前端界面的渠道id
     */
    @Override
    public void trackEvent(Integer id, String ip, Integer userId, String userName, String cid, String mobile, String uuid
            , String jsessionId) {
        if (isBlank(cid)) {
            cid = "app";
        } else if (cid.contains("#")) {
            int i = cid.indexOf('#');
            cid = cid.substring(0, i);
        }

        //1.先记录原始记录
        LmProductCash productCash = insertTrackEventInfo(id, ip, userId, userName, cid, mobile, uuid, jsessionId);

        //传入的id要有效，防止恶意刷接口
        if (productCash != null) {
            //2.使用mq更新渠道的产品点击量
            Message msg1 = new Message();
            msg1.setTopic(topic);
            msg1.setTag("web_update_channel_product_num");
            msg1.setBody(cid.getBytes());
            producer.send(msg1);

            //3.mq查询当日记录，更新
            Message msg2 = new Message();
            msg2.setTopic(topic);
            msg2.setTag("web_update_track_event_pv");

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("productId", id);
            jsonObject.put("ip", ip);
            jsonObject.put("sessionId", uuid);
            jsonObject.put("jsessionId", jsessionId);
            jsonObject.put("limitPreset", productCash.getLimitPreset());
            msg2.setBody(jsonObject.toJSONString().getBytes());
            producer.send(msg2);
        }
    }

    @EventListener(condition = "#event.tag=='web_update_track_event_pv'")
    public void updateTrackEventPv(RocketmqEvent event) {
        JSONObject jsonObject = JSON.parseObject(event.getMsg());

        Integer productId = jsonObject.getInteger("productId");
        String limitPreset = jsonObject.getString("limitPreset");

        String ip = jsonObject.getString("ip");
        String sessionId = jsonObject.getString("sessionId");
        String jsessionId = jsonObject.getString("jsessionId");

        if (productId != null && productId > 0) {
            Date currentDate = new Date();
            //当天开始时间
            Date dataDateBegin = getDayBeginTime(currentDate);
            //当天结束时间
            Date dataDateEnd = getDayEndTime(currentDate);
            Map<String, Object> map = new HashMap<>();
            map.put("productId", productId);
            map.put("dataDateBegin", dataDateBegin);
            map.put("dataDateEnd", dataDateEnd);

            List<Map<String, Object>> trackEventList = getList(map);
            if (trackEventList != null && trackEventList.size() > 0) {
                Map<String, Object> eventMap = trackEventList.get(0);
                if (isNotBlank(eventMap.get("id").toString())) {
                    addOnRedis(productId, ip, sessionId, jsessionId);

                    Long countClick = Long.parseLong(redisClient.get(PREFIX_COUNT_CLICK + productId));

                    Integer countClickDb = (Integer) eventMap.get("countClick");

                    if (countClickDb == null) {
                        countClickDb = 0;
                    }

                    //使用Redis中统计的点击数和数据库中统计的点击数对比，Redis中的数一定比数据库中的数大
                    if (countClick > countClickDb) {
                        Map<String, Object> params = new HashMap<>();
                        params.put("id", eventMap.get("id"));
                        params.put("countClick", countClick);
                        params.put("countIP", redisClient.scard(PREFIX_COUNT_IP + productId));
                        params.put("countSessionId", redisClient.scard(PREFIX_COUNT_SESSION + productId));
                        params.put("countJsessionId", redisClient.scard(PREFIX_COUNT_JSESSION + productId));

                        lmTrackEventDao.updateTrackEventCount(params);
                    } else {
                        int id = (Integer) eventMap.get("id");
                        Date dataDate = (Date) eventMap.get("dataDate");
                        updateTrackEventPvByDate(id, productId, dataDate);
                    }
                }
                //4.判断渠道是否下架
                if (StringUtils.equals("Y", limitPreset)) {
                    Integer trackNum = (Integer) eventMap.get("pv");
                    Integer presetNum = (Integer) eventMap.get("presetNum");
                    if (trackNum + 1 >= presetNum) {
                        LmProductCash lmProductCash1 = new LmProductCash();
                        lmProductCash1.setId(productId);
                        lmProductCashService.updateStatus(lmProductCash1);
                    }
                }
            }
        }
    }

    /**
     * 修改指定日期的track_event的数据
     */
    private void updateTrackEventPvByDate(int id, int productId, Date dataDate) {
        Map<String, Object> params = new HashMap<>();

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        params.put("startDate", simpleDateFormat.format(dataDate));

        Calendar calendar = getInstance();
        calendar.setTime(dataDate);
        calendar.add(DAY_OF_MONTH, 1);
        String endDate = simpleDateFormat.format(calendar.getTime());

        params.put("endDate", endDate);

        params.put("productId", productId);

        Map<String, Object> countMap = lmTrackEventInfoDao.countItems(params);

        params.clear();
        params.put("id", id);
        params.put("countClick", countMap.get("countClick"));
        params.put("countIP", countMap.get("countIp"));
        params.put("countSessionId", countMap.get("countSessionId"));
        params.put("countJsessionId", countMap.get("countJsessionId"));

        lmTrackEventDao.updateTrackEventCount(params);
    }

    /**
     * 使用Redis来统计这些参数，按产品（productId）为单位来统计
     */
    private void addOnRedis(Integer productId, String ip, String sessionId, String jsessionId) {
        long expireTime = getExpireTime();

        String keyCountClick = PREFIX_COUNT_CLICK + productId;

        if (!redisClient.exists(keyCountClick)) {
            redisClient.set(keyCountClick, "1");
            redisClient.expireAt(keyCountClick, expireTime);
        } else {
            redisClient.incr(keyCountClick);
        }

        addOnSet(ip, PREFIX_COUNT_IP + productId, expireTime);
        addOnSet(sessionId, PREFIX_COUNT_SESSION + productId, expireTime);
        addOnSet(jsessionId, PREFIX_COUNT_JSESSION + productId, expireTime);
    }

    /**
     * 向Redis的Set集合中添加数据，并设置过期时间
     */
    private void addOnSet(String value, String key, long expireTime) {
        if (!isEmpty(value)) {
            if (!redisClient.exists(key)) {
                redisClient.sadd(key, value);
                redisClient.expireAt(key, expireTime);
            } else {
                redisClient.sadd(key, value);
            }
        }
    }

    /**
     * 获取Redis中key的过期时间，明日的0点
     *
     * @return 距1970.1.1，00:00:00.000的秒数
     */
    private long getExpireTime() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(DAY_OF_MONTH, 1);
        calendar.set(HOUR_OF_DAY, 0);
        calendar.set(MINUTE, 0);
        calendar.set(SECOND, 0);
        calendar.set(MILLISECOND, 0);

        return calendar.getTimeInMillis() / 1000;
    }

    @EventListener(condition = "#event.tag=='web_update_channel_product_num'")
    public void updateChannelProductNum(RocketmqEvent event) {
        String cid = event.getMsg();
        if (!isBlank(cid)) {
            KwChannel kwChannel = kwChannelService.getByCode(cid);
            // 查出用户渠道并统计到渠道数据表中
            if (kwChannel != null && isNotBlank(kwChannel.getId())) {
                Map<String, String> paramsMap = new HashMap<>();
                paramsMap.put("channelId", kwChannel.getId());
                paramsMap.put("productNum", "1");
                kwChannelDataService.addNum(paramsMap);
            }
        }
    }

    /**
     * 插入原始的访问记录
     */
    private LmProductCash insertTrackEventInfo(Integer id, String ip, Integer userId, String userName, String cid
            , String mobile, String uuid, String jsessionId) {
        LmProductCash productCash = lmProductCashDao.detail(id);
        if (productCash != null) {
            LmTrackEventInfo lmTrackEventInfo = new LmTrackEventInfo();
            lmTrackEventInfo.setUserId(userId);
            lmTrackEventInfo.setUserName(userName);
            lmTrackEventInfo.setProductId(id);
            lmTrackEventInfo.setAction("跳转");
            lmTrackEventInfo.setUserMobile(mobile);
            lmTrackEventInfo.setCid(cid);
            lmTrackEventInfo.setProductName(productCash.getProductName());
            lmTrackEventInfo.setIp(ip);
            lmTrackEventInfo.setSessionId(uuid);
            lmTrackEventInfo.setJsessionId(jsessionId);
            lmTrackEventInfoDao.insert(lmTrackEventInfo);
        }
        return productCash;
    }

}
